package com.cdhkppm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.PrimitiveArrayUtil;
import com.cdhkppm.constant.HKErrorCodeEnum;
import com.cdhkppm.dto.CustomExcelImportActiveStrDTO;
import com.cdhkppm.dto.CustomPlusTaskSaveDTO;
import com.cdhkppm.dto.ImportDevSubTaskDTO;
import com.cdhkppm.dto.ImportTaskDTO;
import com.cdhkppm.mapper.HkGroupMapper;
import com.cdhkppm.service.ExportTaskToExcelService;

import com.cdhkppm.utils.CustomExcelUtils;
import com.cdhkppm.utils.DateUtil;
import com.hustcad.plm.pdm.core.base.EntityBase;
import com.hustcad.plm.pdm.core.context.TyppmUser;
import com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum;
import com.hustcad.plm.rpm.businessdatasecurity.service.ActiveImportDataSecurityHelperBeforeService;
import com.hustcad.plm.rpm.constant.ProjectMngConstant;
import com.hustcad.plm.rpm.model.dto.active.ExcelImportActiveStrDTO;
import com.hustcad.plm.rpm.model.dto.active.PlusTaskSaveDTO;
import com.hustcad.plm.rpm.model.dto.active.TyppmActiveTreeDTO;
import com.hustcad.plm.rpm.model.entity.plan.TyppmPlan;
import com.hustcad.plm.rpm.model.entity.plan.TyppmPlanActive;
import com.hustcad.plm.rpm.service.intekey.InteKeyService;
import com.hustcad.plm.rpm.service.meta.TyppmEnumerationItemService;
import com.hustcad.plm.rpm.service.org.TyppmUserService;
import com.hustcad.plm.rpm.service.plan.TyppmGanttChartService;
import com.hustcad.plm.rpm.service.plan.TyppmPlanActiveService;
import com.hustcad.plm.rpm.service.plan.TyppmPlanService;
import com.hustcad.plm.rpm.serviceextend.ImportDataExtendService;
import com.hustcad.plm.rpm.util.excel.util.ExcelUtils;
import com.ty.basic.constant.TableTypeConstrant;
import com.ty.basic.service.snow.SnowflakeIdComponent;
import com.ty.basic.session.TyAccountContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.cdhkppm.service.impl.CustomTyppmImportDataServiceImpl.initExportPlanColumnToExcel;

/**
 * @author yfq
 * @apiNote
 */
@Slf4j
@Service
public class ExportTaskToExcelServiceImpl implements ExportTaskToExcelService {

    @Resource
    private TyppmPlanService typpmPlanService;
    @Resource
    private TyppmPlanActiveService typpmPlanActiveService;

    @Resource
    private TyppmEnumerationItemService typpmEnumerationItemService;

    @Resource
    private ActiveImportDataSecurityHelperBeforeService activeImportDataSecurityHelperBeforeService;

    @Resource
    private ImportDataExtendService importDataExtendService;


    @Value("${isEncryption:false}")
    private boolean isEncryption;

    @Resource
    private InteKeyService inteKeyService;
    @Resource
    private SnowflakeIdComponent snowflakeIdComponent;
    @Resource
    private TyppmUserService typpmUserService;

    @Resource
    private TyppmGanttChartService ganttChartService;

    @Resource
    private HkGroupMapper hkGroupMapper;

    @Override
    public void exportTaskToExcel(HttpServletRequest request, HttpServletResponse response,
                                  List<CustomPlusTaskSaveDTO> exportUserTaskDTOList, String pjoName)
            throws IOException {

        if (CollectionUtils.isEmpty(exportUserTaskDTOList)) {
            log.info("任务为空");
            return;
        }

        String planOid = exportUserTaskDTOList.get(0).getParentOID();
        if (StringUtils.isBlank(planOid)) {
            log.info("没有此项目");
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("没有此项目");
        }

        TyppmPlan plan = this.typpmPlanService.selectOneByOID(planOid);
        if (Objects.isNull(plan)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("没有此项目计划");
        }

        Map<String, Object> params = new HashMap<>();
        params.put("rootOID", planOid);
        params.put("rootOTYPE", "ty.projectmng.admin.CTyPlan");
        params.put("markfordelete", 0L);

        List<TyppmActiveTreeDTO> treeDTOList = this.typpmPlanActiveService.queryActiveTreeDTOListByParams(params);
        if (Objects.isNull(treeDTOList)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("没有此项目计划");
        }

        String targetOutline;
        List<TyppmActiveTreeDTO> filteredTreeList = new ArrayList<>();
        if (pjoName.contains("项目内单机设备")) {
            targetOutline = treeDTOList.stream()
                    .filter(dto -> dto.getName() != null && dto.getName().contains("项目内单机设备"))
                    .findFirst()
                    .map(TyppmPlanActive::getOutLineNumber)
                    .orElse(null);

            if (StringUtils.isBlank(targetOutline)) {
                throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("未找到【项目内单机设备】任务");
            }

            String finalTargetOutline = targetOutline;
            int targetDepth = finalTargetOutline.split("\\.").length;

            filteredTreeList = treeDTOList.stream()
                    .filter(dto -> dto.getOutLineNumber() != null
                            && dto.getOutLineNumber().startsWith(finalTargetOutline + "."))
                    .filter(dto -> {
                        int depth = dto.getOutLineNumber().split("\\.").length;
                        return depth == targetDepth + 1 || depth == targetDepth + 2;
                    })
                    .collect(Collectors.toList());
        }

        if (pjoName.contains("单机设备任务")) {
            // 找出所有“项目内单机设备”节点
            List<TyppmActiveTreeDTO> deviceProjects = treeDTOList.stream()
                    .filter(dto -> dto.getName() != null && dto.getName().contains("项目内单机设备"))
                    .collect(Collectors.toList());

            if (deviceProjects.isEmpty()) {
                throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("未找到【项目内单机设备】节点");
            }

            List<TyppmActiveTreeDTO> hkDevTasks = new ArrayList<>();

            for (TyppmActiveTreeDTO deviceProject : deviceProjects) {
                String deviceOutline = deviceProject.getOutLineNumber();

                List<TyppmActiveTreeDTO> devTasksUnderDevice = treeDTOList.stream()
                        .filter(dto -> dto.getName() != null
                                && dto.getName().contains("HK")
                                && dto.getName().contains("设计")
                                && dto.getOutLineNumber().startsWith(deviceOutline + "."))
                        .collect(Collectors.toList());

                hkDevTasks.addAll(devTasksUnderDevice);
            }

            if (hkDevTasks.isEmpty()) {
                throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("未找到匹配的单机设备任务");
            }

            // 只添加 HK 开发任务自身，不包含子节点
            filteredTreeList.addAll(hkDevTasks);
        }

        if (CollectionUtils.isEmpty(filteredTreeList)) {
            throw HKErrorCodeEnum.EXPORT_TYPE_NOT_EXIST.getException("未找到符合条件的任务或没有权限导出");
        }

        filteredTreeList = filteredTreeList.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(
                                () -> new TreeSet<>(Comparator.comparing(TyppmPlanActive::getLineNumber))),
                        ArrayList::new
                ));

        if (CollectionUtils.isEmpty(filteredTreeList)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("未找到子任务或无执行人负责人");
        }

        List<ExcelImportActiveStrDTO> result =
                this.activeTreeListToExcelImportDto(filteredTreeList, exportUserTaskDTOList);

        List<ExcelImportActiveStrDTO> filteredResult;
        String name = TyAccountContext.getUser().getName();
        if (pjoName.contains("项目内单机设备")) {
            filteredResult = result.stream()
                    .filter(dto -> {
                        String level = dto.getOutLineNumber();
                        String ownerName = dto.getOwnerName();
                        String executorName = dto.getExecutorName();
                        return level != null
                                && level.matches("\\d+\\.\\d+\\.\\d+")
                                && (
                                (StringUtils.isNotBlank(ownerName) && ownerName.trim().equalsIgnoreCase(name.trim()))
                                        || (StringUtils.isNotBlank(executorName) && executorName.trim().equalsIgnoreCase(name.trim()))
                        );
                    })
                    .collect(Collectors.toList());
        }
        else {
            filteredResult = result.stream()
                    .filter(dto -> {
                        String ownerName = dto.getOwnerName();
                        String activeName = dto.getActiveName();
                        String executorName = dto.getExecutorName();

                        return (
                                (StringUtils.isNotBlank(ownerName) && ownerName.trim().equalsIgnoreCase(name.trim()))
                                        || (StringUtils.isNotBlank(executorName) && executorName.trim().equalsIgnoreCase(name.trim()))
                        )
                                && (activeName == null || !activeName.contains("设计实现"));
                    })
                    .collect(Collectors.toList());
        }

        Map<String, String> fieldMap = initExportPlanColumnToExcel();
        if (pjoName.contains("单机设备任务")) {
            fieldMap.remove("mfgLeader");
            fieldMap.remove("mfgPlanStartTime");
            fieldMap.remove("mfgPlanEndTime");
            fieldMap.remove("purchaseLeader");
            fieldMap.remove("purchasePlanStartTime");
            fieldMap.remove("purchasePlanEndTime");
        }
        this.activeImportDataSecurityHelperBeforeService.exportPlanActiveToExcelAfter(filteredTreeList, filteredResult,
                                                                                      fieldMap);
        this.importDataExtendService.exportPlanActiveToExcelAfter(filteredTreeList, filteredResult, fieldMap);

        Workbook sheets = CustomExcelUtils.listToExcelWithOutIndex(filteredResult, fieldMap);
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        String fileName = plan.getName() + "_" + LocalDateTime.now().format(dtf);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            sheets.write(bos);
            bos.flush();
        } finally {
            bos.close();
        }

        byte[] bytes = bos.toByteArray();
        if (PrimitiveArrayUtil.isEmpty(bytes)) {
            throw SystemErrorCodeEnum.EXCEL_FIELD_MAP_EMPTY.message();
        } else {
            String srcFileName = fileName + ".xlsx";
            if (this.isEncryption) {
                bytes = this.inteKeyService.encrypt(sheets, srcFileName);
            }
            this.outputExcel(request, response, bytes, srcFileName);
        }
    }


    @Override
    public void importSubTaskFromExcel(MultipartFile file, List<CustomPlusTaskSaveDTO> exportUserTaskDTOList) {
        String planOid = exportUserTaskDTOList.get(0).getParentOID();
        String containerOID = exportUserTaskDTOList.get(0).getContainerOID();
        if (StringUtils.isBlank(planOid)) {
            log.info("没有此项目");
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("没有此项目");
        }
        List<CustomPlusTaskSaveDTO> allTasks = new ArrayList<>();
        for (CustomPlusTaskSaveDTO task : exportUserTaskDTOList) {
            allTasks.addAll(flattenTasks(task));
        }
        log.info("任务总数量（含子级）：{}", allTasks.size());
        Map<String, Object> params = new HashMap<>();
        params.put("rootOID", planOid);
        params.put("rootOTYPE", "ty.projectmng.admin.CTyPlan");
        params.put("markfordelete", 0L);
        List<TyppmActiveTreeDTO> treeDTOList = this.typpmPlanActiveService.queryActiveTreeDTOListByParams(params);
        if (CollectionUtils.isEmpty(treeDTOList)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("没有此项目计划");
        }

        TyppmActiveTreeDTO parentTask = treeDTOList.stream()
                .filter(dto -> dto.getName() != null && dto.getName().contains("项目内单机设备"))
                .findFirst()
                .orElse(null);
        if (Objects.isNull(parentTask)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("未找到【项目内单机设备开发制造】任务");
        }
        String parentOutline = parentTask.getOutLineNumber();

        List<TyppmActiveTreeDTO> childList = treeDTOList.stream()
                .filter(dto -> StringUtils.isNotBlank(dto.getOutLineNumber()))
                .filter(dto -> dto.getOutLineNumber().startsWith(parentOutline + "."))
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(childList)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("【项目内单机设备开发制造】未找到子级任务");
        }
        log.info("找到的子任务数量：{}", childList.size());

        boolean hasValidTask = false;
        List<String> errorMessages = new ArrayList<>();

        List<ImportTaskDTO> importTaskDTOS = com.ty.basic.utils.excel.ExcelUtils.excelToList(file, ImportTaskDTO.class);
        if (CollectionUtils.isEmpty(importTaskDTOS)) {
            throw HKErrorCodeEnum.IMPORTTASK_TYPE_NOT_EXIST.getException("没有数据");
        }

        for (int i = 0; i < importTaskDTOS.size(); i++) {
            ImportTaskDTO importDTO = importTaskDTOS.get(i);
            String isAssigned = importDTO.getIsAssigned();
            if ("是".equalsIgnoreCase(isAssigned)){
                continue;
            }
            String devLeader = importDTO.getDevLeader();
            String mfgLeader = importDTO.getMfgLeader();
            String purchaseLeader = importDTO.getPurchaseLeader();

            if (StringUtils.isAllBlank(devLeader, mfgLeader, purchaseLeader)) {
                errorMessages.add("第 " + (i + 1) + " 行负责人均为空，未生成子级任务");
                continue;
            }

            // 找对应的父任务（根据导入表中的任务名匹配）
            TyppmActiveTreeDTO parent = childList.stream()
                    .filter(dto -> dto.getName().equals(importDTO.getTaskName()))
                    .findFirst()
                    .orElse(null);

            if (Objects.isNull(parent)) {
                errorMessages.add("第 " + (i + 1) + " 行未匹配到父任务：" + importDTO.getTaskName());
                continue;
            }

            // 每个非空负责人生成一个子任务
            if (StringUtils.isNotBlank(devLeader)) {
                CustomPlusTaskSaveDTO devTask = buildChildTask(parent, "[设计]",
                                                               devLeader, importDTO.getDevPlanStartTime(),
                                                               importDTO.getDevPlanEndTime(), containerOID, "normal");
                allTasks.add(devTask);
                hasValidTask = true;
            }

            if (StringUtils.isNotBlank(mfgLeader)) {
                CustomPlusTaskSaveDTO mfgTask = buildChildTask(parent, "[制造]",
                                                               mfgLeader, importDTO.getMfgPlanStartTime(),
                                                               importDTO.getMfgPlanEndTime(), containerOID, "normal");
                allTasks.add(mfgTask);
                hasValidTask = true;
            }

            if (StringUtils.isNotBlank(purchaseLeader)) {
                CustomPlusTaskSaveDTO purchaseTask = buildChildTask(parent, "[外购]",
                                                                    purchaseLeader,
                                                                    importDTO.getPurchasePlanStartTime(),
                                                                    importDTO.getPurchasePlanEndTime(), containerOID,
                                                                    "normal");
                allTasks.add(purchaseTask);
                hasValidTask = true;
            }
        }

        if (!hasValidTask && !errorMessages.isEmpty()) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException(
                    "所有任务导入失败:\n" + String.join("\n", errorMessages));
        }

        reorderByOutLineNumberAndIndex(allTasks);
        log.info("任务已按 outLineNumber 排序并重新编号，共 {} 条任务", allTasks.size());
        List<PlusTaskSaveDTO> plusTaskSaveDTOS = convertToPlusTaskList(allTasks);
        ganttChartService.saveTableTreeEx(planOid, plusTaskSaveDTOS);

    }

    @Override
    public void importDevSubSubTaskFromExcel(MultipartFile file, List<CustomPlusTaskSaveDTO> exportUserTaskDTOList) {
        String planOid = exportUserTaskDTOList.get(0).getParentOID();
        String containerOID = exportUserTaskDTOList.get(0).getContainerOID();
        if (StringUtils.isBlank(planOid)) {
            log.info("没有此项目");
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("没有此项目");
        }
        List<CustomPlusTaskSaveDTO> allTasks = new ArrayList<>();
        for (CustomPlusTaskSaveDTO task : exportUserTaskDTOList) {
            allTasks.addAll(flattenTasks(task));
        }
        log.info("任务总数量（含子级）：{}", allTasks.size());
        Map<String, Object> params = new HashMap<>();
        params.put("rootOID", planOid);
        params.put("rootOTYPE", "ty.projectmng.admin.CTyPlan");
        params.put("markfordelete", 0L);
        List<TyppmActiveTreeDTO> treeDTOList = this.typpmPlanActiveService.queryActiveTreeDTOListByParams(params);
        if (CollectionUtils.isEmpty(treeDTOList)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("没有此项目计划");
        }

        TyppmActiveTreeDTO parentTask = treeDTOList.stream()
                .filter(dto -> dto.getName() != null && dto.getName().contains("项目内单机设备"))
                .findFirst()
                .orElse(null);
        if (Objects.isNull(parentTask)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("未找到【项目内单机设备开发制造】任务");
        }
        String parentOutline = parentTask.getOutLineNumber();

        List<TyppmActiveTreeDTO> childList = treeDTOList.stream()
                .filter(dto -> StringUtils.isNotBlank(dto.getOutLineNumber()))
                .filter(dto -> dto.getOutLineNumber().startsWith(parentOutline + "."))
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(childList)) {
            throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("【项目内单机设备开发制造】未找到子级任务");
        }
        log.info("找到的子任务数量：{}", childList.size());


        List<ImportDevSubTaskDTO> importTaskDTOS = com.ty.basic.utils.excel.ExcelUtils.excelToList(file,
                                                                                                   ImportDevSubTaskDTO.class);
        if (CollectionUtils.isEmpty(importTaskDTOS)) {
            throw HKErrorCodeEnum.IMPORTTASK_TYPE_NOT_EXIST.getException("没有数据");
        }

        for (int i = 0; i < importTaskDTOS.size(); i++) {
            ImportDevSubTaskDTO importDTO = importTaskDTOS.get(i);
            String isAssigned = importDTO.getIsAssigned();
            if ("是".equalsIgnoreCase(isAssigned)){
                continue;
            }
            String devLeader = importDTO.getDevLeader();
            if (StringUtils.isBlank(devLeader)) {
                log.info("第 {} 行开发负责人为空，跳过生成【开发实现】任务", i + 1);
                continue;
            }

            TyppmActiveTreeDTO parent = childList.stream()
                    .filter(dto -> dto.getName().equals(importDTO.getTaskName()))
                    .findFirst()
                    .orElse(null);

            if (Objects.isNull(parent)) {
                log.info("第 {} 行任务不存在，跳过生成【开发实现】任务", i + 1);
                throw HKErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("第 " + (i + 1) + " 行任务不存在");
            }


            CustomPlusTaskSaveDTO devSubTask = buildChildTask(
                    parent,
                    "[设计实现]",
                    devLeader,
                    importDTO.getDevPlanStartTime(),
                    importDTO.getDevPlanEndTime(),
                    containerOID,
                    "devSub"
            );
            allTasks.add(devSubTask);

        }

        reorderByOutLineNumberAndIndex(allTasks);
        log.info("任务已按 outLineNumber 排序并重新编号，共 {} 条任务", allTasks.size());
        List<PlusTaskSaveDTO> plusTaskSaveDTOS = convertToPlusTaskList(allTasks);
        ganttChartService.saveTableTreeEx(planOid, plusTaskSaveDTOS);
    }


    private CustomPlusTaskSaveDTO buildChildTask(
            TyppmActiveTreeDTO parent,
            String suffix,
            String leader,
            String planStart,
            String planEnd,
            String containerOID,
            String type
    ) {
        CustomPlusTaskSaveDTO childTask = new CustomPlusTaskSaveDTO();

        String parentName = parent.getName();
        String baseName = parentName.contains("[")
                ? parentName.substring(0, parentName.indexOf("["))
                : parentName;

        childTask.setName(baseName + suffix);

        // 从 Excel 读取并格式化
        String datePlanStart = DateUtil.formatExcelDate(planStart);
        String datePlanEnd = DateUtil.formatExcelDate(planEnd);

        if (StringUtils.isBlank(datePlanStart)) {
            datePlanStart = String.valueOf(parent.getStartDate());
        }
        if (StringUtils.isBlank(datePlanEnd)) {
            datePlanEnd = String.valueOf(parent.getFinishDate());
        }

        childTask.setEarlyStart(datePlanStart);
        childTask.setEarlyFinish(datePlanEnd);
        childTask.setLateStart(datePlanStart);
        childTask.setLateFinish(datePlanEnd);
        childTask.setStartDate(datePlanStart);
        childTask.setFinishDate(datePlanEnd);


        childTask.setActive("1");
        childTask.setConstraintType("0");
        childTask.setCritical("0");
        childTask.setOutLineLevel("1");
        childTask.setOutLineNumber(parent.getOutLineNumber() + ".1");
        childTask.setOutLineLevel(String.valueOf(parent.getOutLineLevel() + 1));
        childTask.setLineNumber(String.valueOf(parent.getLineNumber() + 1));
        childTask.setWork("0");
        childTask.setPercentWorkComplete("0");
        childTask.setParentOID(parent.getOid());
        childTask.setParentOTYPE(parent.getParentOTYPE());
        childTask.setTypeOID(parent.getTypeOID());
        childTask.setTypeOTYPE(TableTypeConstrant.TY_TYPE_OTYPE);
        childTask.setOid(snowflakeIdComponent.getInstance().nextId().toString());
        childTask.setOtype(TableTypeConstrant.TY_PLAN_ACTIVITY_OTYPE);
        childTask.setJobOID(snowflakeIdComponent.getInstance().nextId().toString());
        childTask.setHealthStatusType("UNAVAILABLE");
        childTask.setControlled("0");
        childTask.setMarkForDelete("0");
        childTask.setEffortDriven("0");


        //TODO 用户
        TyppmUser typpmUser = typpmUserService.checkUserRemote(leader);
        if (Objects.isNull(typpmUser)) {
            throw HKErrorCodeEnum.USER_NOT_EXIST.getException("用户不存在");
        }
        if ("devSub".equalsIgnoreCase(type)) {
            String oidModule = hkGroupMapper.getGroupNameAndPjOid("设计模块", containerOID,leader);
            String oidLeader = hkGroupMapper.getGroupNameAndPjOid("设计组长", containerOID,leader);
            String useOid = StringUtils.isNotBlank(oidModule)
                    ? oidModule
                    : oidLeader;

            if (StringUtils.isBlank(useOid)) {
                throw HKErrorCodeEnum.GROUP_NOT_EXIST
                        .getException(leader+"用户所在组不存在[设计模块/设计组长]");
            }

            childTask.setOwnerOID(parent.getExecutorOID());
            childTask.setOwnerGroupOid(parent.getExecutorGroupOid());
            childTask.setExecutorOID(typpmUser.getOid());
            childTask.setExecutorGroupOid(useOid);
        }
        if ("normal".equalsIgnoreCase(type)) {
            String name = childTask.getName();
            if (name.contains("设计")) {
                String oidModule = hkGroupMapper.getGroupNameAndPjOid("设计模块", containerOID,leader);
                String oidLeader = hkGroupMapper.getGroupNameAndPjOid("设计组长", containerOID,leader);
                String useOid = StringUtils.isNotBlank(oidModule)
                        ? oidModule
                        : oidLeader;

                if (StringUtils.isBlank(useOid)) {
                    throw HKErrorCodeEnum.GROUP_NOT_EXIST
                            .getException(leader+"用户所在组不存在[设计模块/设计组长]");
                }

                childTask.setOwnerOID(parent.getExecutorOID());
                childTask.setOwnerGroupOid(parent.getExecutorGroupOid());
                childTask.setExecutorOID(typpmUser.getOid());
                childTask.setExecutorGroupOid(useOid);
            }

            if (name.contains("外购")) {
                String oidModule = hkGroupMapper.getGroupNameAndPjOid("外购负责人模块", containerOID,leader);
                String oidLeader = hkGroupMapper.getGroupNameAndPjOid("外购负责人组长", containerOID,leader);
                String useOid = StringUtils.isNotBlank(oidModule)
                        ? oidModule
                        : oidLeader;

                if (StringUtils.isBlank(useOid)) {
                    throw HKErrorCodeEnum.GROUP_NOT_EXIST
                            .getException(leader+"用户所在组不存在[外购负责人模块/外购负责人组长]");
                }

                childTask.setOwnerOID(parent.getExecutorOID());
                childTask.setOwnerGroupOid(parent.getExecutorGroupOid());
                childTask.setExecutorOID(typpmUser.getOid());
                childTask.setExecutorGroupOid(useOid);
            }
            if (name.contains("制造")) {
                String oidModule = hkGroupMapper.getGroupNameAndPjOid("制造负责模块", containerOID,leader);
                String oidLeader = hkGroupMapper.getGroupNameAndPjOid("制造负责人组长", containerOID,leader);
                String useOid = StringUtils.isNotBlank(oidModule)
                        ? oidModule
                        : oidLeader;

                if (StringUtils.isBlank(useOid)) {
                    throw HKErrorCodeEnum.GROUP_NOT_EXIST
                            .getException(leader+"用户所在组不存在[制造负责模块/制造负责人组长]");
                }

                childTask.setOwnerOID(parent.getExecutorOID());
                childTask.setOwnerGroupOid(parent.getExecutorGroupOid());
                childTask.setExecutorOID(typpmUser.getOid());
                childTask.setExecutorGroupOid(useOid);
            }
        }

        return childTask;
    }



    private List<PlusTaskSaveDTO> convertToPlusTaskList(List<CustomPlusTaskSaveDTO> customList) {
        List<PlusTaskSaveDTO> plusList = new ArrayList<>();

        DateTimeFormatter isoFormatterWithMillisAndZone = DateTimeFormatter.ofPattern(
                "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        DateTimeFormatter isoFormatterWithMillis = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
        DateTimeFormatter isoFormatterWithoutMillis = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm");
        DateTimeFormatter standardFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter isoFormatterWithoutMillisAndZone =
                DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

        DateTimeFormatter finalFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        for (CustomPlusTaskSaveDTO custom : customList) {
            PlusTaskSaveDTO plus = new PlusTaskSaveDTO();
            BeanUtils.copyProperties(custom, plus);

            String earlyStart = custom.getEarlyStart();
            String plannedEndTime = custom.getFinishDate();
            String actualStartDate = custom.getActualStartDate();
            String actualFinishDate = custom.getActualFinishDate();

            LocalDate startTime = parseDateWithMultipleFormats(
                    earlyStart,
                    isoFormatterWithMillisAndZone,
                    isoFormatterWithMillis,
                    isoFormatterWithoutMillisAndZone,
                    isoFormatterWithoutMillis,
                    standardFormatter,
                    dateFormatter
            );
            LocalDateTime startDateTime = startTime.atTime(0, 0, 0);
            String currentStartTime = finalFormatter.format(startDateTime);

            LocalDate endDate = parseDateWithMultipleFormats(
                    plannedEndTime,
                    isoFormatterWithMillisAndZone,
                    isoFormatterWithMillis,
                    isoFormatterWithoutMillisAndZone,
                    isoFormatterWithoutMillis,
                    standardFormatter,
                    dateFormatter
            );

            endDate = DateUtil.adjustToNextWorkday(endDate);
            LocalDateTime endDateTime = endDate.atTime(23, 59, 59);
            String currentEndTime = finalFormatter.format(endDateTime);


            plus.setEarlyStart(currentStartTime);
            plus.setConstraintDate(currentStartTime);
            plus.setStartDate(currentStartTime);

            plus.setEarlyFinish(currentEndTime);
            plus.setFinishDate(currentEndTime);


            if (actualStartDate != null && !actualStartDate.isEmpty()) {
                LocalDate asDate = parseDateWithMultipleFormats(
                        actualStartDate,
                        isoFormatterWithMillisAndZone,
                        isoFormatterWithMillis,
                        isoFormatterWithoutMillisAndZone,
                        isoFormatterWithoutMillis,
                        standardFormatter,
                        dateFormatter
                );
                String formattedActualStart = finalFormatter.format(asDate.atTime(0, 0, 0));
                plus.setActualStartDate(formattedActualStart);
            }


            if (actualFinishDate != null && !actualFinishDate.isEmpty()) {
                LocalDate afDate = parseDateWithMultipleFormats(
                        actualFinishDate,
                        isoFormatterWithMillisAndZone,
                        isoFormatterWithMillis,
                        isoFormatterWithoutMillisAndZone,
                        isoFormatterWithoutMillis,
                        standardFormatter,
                        dateFormatter
                );
                String formattedActualFinish = finalFormatter.format(afDate.atTime(23, 59, 59));
                plus.setActualFinishDate(formattedActualFinish);
            }


            long durationMillis = DateUtil.calculateWorkdayDuration(currentStartTime, currentEndTime);


            String lateFinish = DateUtil.formatLocalDateTime(LocalDateTime.parse(
                    Objects.requireNonNull(DateUtil.getProjectEndDateWithTime(custom.getLateFinish())),
                    DateUtil.DATE_TIME_FORMATTER));
            plus.setLateFinish(lateFinish);

            plus.setDuration(String.valueOf(durationMillis));
            plus.setLateStart(DateUtil.formatLocalDateTime(DateUtil.calculateLateStartFromMillis(
                    LocalDateTime.parse(lateFinish, DateUtil.DATE_TIME_FORMATTER),
                    durationMillis
            )));

            plusList.add(plus);
        }

        return plusList;
    }



    private LocalDate parseDateWithMultipleFormats(String dateStr, DateTimeFormatter... formatters) {

        for (DateTimeFormatter formatter : formatters) {
            try {
                return LocalDate.parse(dateStr, formatter);
            } catch (DateTimeParseException e1) {
                if (formatter.toString().contains("H")) {
                    try {
                        return LocalDateTime.parse(dateStr, formatter).toLocalDate();
                    } catch (DateTimeParseException e2) {
                        continue;
                    }
                }
            }
        }

        throw HKErrorCodeEnum.IMPORTTASK_TYPE_NOT_EXIST
                .getException("无法解析日期: " + dateStr);
    }



    private void reorderByOutLineNumberAndIndex(List<CustomPlusTaskSaveDTO> allTasks) {
        if (CollectionUtils.isEmpty(allTasks)) {
            return;
        }

        // 记录每个任务的原始索引位置
        Map<CustomPlusTaskSaveDTO, Integer> indexMap = new HashMap<>();
        for (int i = 0; i < allTasks.size(); i++) {
            indexMap.put(allTasks.get(i), i);
        }

        // 排序逻辑
        allTasks.sort((a, b) -> {
            String outA = StringUtils.defaultString(a.getOutLineNumber());
            String outB = StringUtils.defaultString(b.getOutLineNumber());
            int cmp = outA.compareTo(outB);
            if (cmp != 0) {
                return cmp;
            }
            // 相同则按原始顺序
            return Integer.compare(indexMap.get(a), indexMap.get(b));
        });

        // 重新编号 lineNumber：1、2、3、4、5……
        for (int i = 0; i < allTasks.size(); i++) {
            allTasks.get(i).setLineNumber(String.valueOf(i + 1));
        }

        log.info("LineNumber 重排完成，总数：{}", allTasks.size());
    }

    /**
     * 递归展开所有子任务，包含自身
     */
    private List<CustomPlusTaskSaveDTO> flattenTasks(CustomPlusTaskSaveDTO parentTask) {
        List<CustomPlusTaskSaveDTO> flatList = new ArrayList<>();
        flatList.add(parentTask);

        if (parentTask.getChildren() != null) {
            for (CustomPlusTaskSaveDTO child : parentTask.getChildren()) {
                flatList.addAll(flattenTasks(child));
            }
        }
        return flatList;
    }


    private List<ExcelImportActiveStrDTO> activeTreeListToExcelImportDto(List<TyppmActiveTreeDTO> treeDTOList,
                                                                         List<CustomPlusTaskSaveDTO> exportUserTaskDTOList) {
        List<ExcelImportActiveStrDTO> result = new ArrayList<>();
        if (CollUtil.isEmpty(treeDTOList)) {
            return result;
        } else {
            Map activeTreeDTOMap = treeDTOList.stream().collect(Collectors.toMap(
                    EntityBase::getOid, Function.identity(), (a, b) -> a));
            Map<String, String> roleMap = this.typpmEnumerationItemService.getEnumDisplayByDefAndItemKey("role", "");

            for (int i = 0; i < treeDTOList.size(); ++i) {
                TyppmActiveTreeDTO treeDTO = treeDTOList.get(i);

                CustomExcelImportActiveStrDTO excelDTO = new CustomExcelImportActiveStrDTO();
                this.copyActiveDtoAttributeToExcelDto(excelDTO, treeDTO, roleMap, exportUserTaskDTOList);
                excelDTO.setIndex(String.valueOf(i + NumberUtils.INTEGER_ONE));
                excelDTO.setBeforeLineNumber(this.getActiveFormatBeforeLineNumber(treeDTO, activeTreeDTOMap));
                result.add(excelDTO);
            }

            return result;
        }
    }

    private void copyActiveDtoAttributeToExcelDto(CustomExcelImportActiveStrDTO excelDTO,
                                                  TyppmActiveTreeDTO treeDTO,
                                                  Map<String, String> roleMap,
                                                  List<CustomPlusTaskSaveDTO> exportUserTaskDTOList) {

        excelDTO.setActiveName(treeDTO.getName());
        excelDTO.setOwnerGroupDisplayName(roleMap.get(treeDTO.getOwnerGroupKey()));

        String executorName = treeDTO.getExecutorName();
        if (CharSequenceUtil.isNotBlank(executorName) &&
                executorName.contains("(") && executorName.contains(")")) {
            executorName = executorName.substring(executorName.indexOf("(") + 1, executorName.lastIndexOf(")"));
        }
        excelDTO.setExecutorName(executorName);

        excelDTO.setExecutorGroupDisplayName(roleMap.get(treeDTO.getExecutorGroupKey()));
        excelDTO.setOwnerName(treeDTO.getOwnerName());
        excelDTO.setOutLineLevel(String.valueOf(treeDTO.getOutLineLevel()));
        excelDTO.setOutLineNumber(treeDTO.getOutLineNumber());
        excelDTO.setWbsNumber(treeDTO.getWbs());
        excelDTO.setControlled(treeDTO.getControlled());
        excelDTO.setMilestone(treeDTO.getMilestone());
        excelDTO.setDeadline(treeDTO.getDeadline());
        excelDTO.setStartDate(treeDTO.getStartDate());
        excelDTO.setFinishDate(treeDTO.getFinishDate());
        excelDTO.setSecurityLabels(treeDTO.getSecurityLabels());

        if (Objects.nonNull(treeDTO.getRatedHours())) {
            long hour = treeDTO.getRatedHours() / 3600000L;
            excelDTO.setPeriod(String.valueOf(hour));
        }
        if (Objects.nonNull(treeDTO.getDoneEffortMillis()) && treeDTO.getDoneEffortMillis() != 0L) {
            long hour = treeDTO.getDoneEffortMillis() / 3600000L;
            excelDTO.setActualPeriod(String.valueOf(hour));
        }

        boolean hasAssignedChild = false; // 用于判断子任务是否已分配

        if (CollUtil.isNotEmpty(exportUserTaskDTOList)) {
            CustomPlusTaskSaveDTO matchedTask = findMatchedTask(exportUserTaskDTOList, treeDTO.getJobOID());

            if (matchedTask != null) {
                applyIbaToExcel(excelDTO, matchedTask.getIbaAttribute());

                if (CollUtil.isNotEmpty(matchedTask.getChildren())) {
                    for (CustomPlusTaskSaveDTO child : matchedTask.getChildren()) {
                        String childName = child.getName();
                        String executorOID = child.getExecutorOID();
                        TyppmUser typpmUser = typpmUserService.selectOneByOID(executorOID);
                        String ownerName = typpmUser.getName();
                        String startStr = child.getStartDate();
                        String endStr = child.getFinishDate();

                        if (StringUtils.isNotBlank(ownerName)) {
                            hasAssignedChild = true;
                        }

                        if (startStr != null) {
                            OffsetDateTime odt = OffsetDateTime.parse(startStr);
                            LocalDate localDate = odt.toLocalDate();
                            startStr = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                        }
                        if (endStr != null) {
                            OffsetDateTime odt = OffsetDateTime.parse(endStr);
                            LocalDate localDate = odt.toLocalDate();
                            endStr = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                        }

                        if (childName.contains("设计")) {
                            excelDTO.setDevLeader(ownerName);
                            excelDTO.setDevPlanStartTime(startStr);
                            excelDTO.setDevPlanEndTime(endStr);
                        } else if (childName.contains("制造")) {
                            excelDTO.setMfgLeader(ownerName);
                            excelDTO.setMfgPlanStartTime(startStr);
                            excelDTO.setMfgPlanEndTime(endStr);
                        } else if (childName.contains("外购")) {
                            excelDTO.setPurchaseLeader(ownerName);
                            excelDTO.setPurchasePlanStartTime(startStr);
                            excelDTO.setPurchasePlanEndTime(endStr);
                        }
                    }
                }
            } else {
                applyIbaToExcel(excelDTO, null);
            }
        }

        excelDTO.setIsAssigned(hasAssignedChild ? "是" : "");
    }





    private CustomPlusTaskSaveDTO findMatchedTask(List<CustomPlusTaskSaveDTO> list, String jobOid) {
        if (CollUtil.isEmpty(list) || StringUtils.isBlank(jobOid)) {
            return null;
        }
        for (CustomPlusTaskSaveDTO t : list) {
            if (Objects.equals(t.getJobOID(), jobOid)) {
                return t;
            }
            if (CollUtil.isNotEmpty(t.getChildren())) {
                CustomPlusTaskSaveDTO r = findMatchedTask(t.getChildren(), jobOid);
                if (r != null) {
                    return r;
                }
            }
        }
        return null;
    }

    private void applyIbaToExcel(CustomExcelImportActiveStrDTO dto, Map<String, List<String>> iba) {
        if (iba == null) {
            dto.setQuantity("");
            dto.setProcessSection("");
            dto.setProductUnitSystem("");
            dto.setProductName("");
            dto.setProductModel("");
            dto.setProductForm("");
            return;
        }
        dto.setQuantity(getIbaValue(iba, "quantity"));
        dto.setProcessSection(getIbaValue(iba, "processSection"));
        dto.setProductUnitSystem(getIbaValue(iba, "productUnitSystem"));
        dto.setProductName(getIbaValue(iba, "productName"));
        dto.setProductModel(getIbaValue(iba, "productModel"));
        dto.setProductForm(getIbaValue(iba, "productForm"));
    }


    private String getActiveFormatBeforeLineNumber(TyppmActiveTreeDTO treeDTO,
                                                   Map<String, TyppmActiveTreeDTO> activeTreeDTOMap) {
        String splitChar = ",";
        String result = "";
        if (CharSequenceUtil.isBlank(treeDTO.getBeforeOID())) {
            return result;
        } else {
            List<String> resultList = new ArrayList();
            String[] oidSplit = treeDTO.getBeforeOID().split(splitChar);
            long[] durationSplit = CharSequenceUtil.splitToLong(treeDTO.getBeforeDurationMillis(), splitChar);
            long[] typeSplit = CharSequenceUtil.splitToLong(treeDTO.getBeforePrecedenceType(), splitChar);

            for (int i = 0; i < oidSplit.length; ++i) {
                String oid = oidSplit[i];
                TyppmActiveTreeDTO dto = activeTreeDTOMap.get(oid);
                String lineNumber = String.valueOf(dto.getLineNumber());
                Long duration = 0L;
                if (durationSplit.length > i) {
                    long l = durationSplit[i] / 28800000L;
                    duration = l;
                }

                String type = ProjectMngConstant.getPrecedenceTypeValueMap(ProjectMngConstant.PRECEDENCE_TYPE_FS)
                        .toUpperCase();
                if (typeSplit.length > i) {
                    String precedenceTypeValue = ProjectMngConstant.getPrecedenceTypeValueMap(typeSplit[i]);
                    if (StringUtils.isNotBlank(precedenceTypeValue)) {
                        type = precedenceTypeValue.toUpperCase(Locale.US);
                    }
                }

                resultList.add(this.formatBeforeLineString(lineNumber, duration, type));
            }

            return CharSequenceUtil.join(",", resultList);
        }
    }

    private String formatBeforeLineString(String lineNumber, Long duration, String type) {
        String op = "";
        if (duration > 0L) {
            op = "+" + Math.abs(duration);
        } else if (duration < 0L) {
            op = "-" + Math.abs(duration);
        }

        return lineNumber + type + op;
    }


    private String getIbaValue(Map<String, List<String>> ibaMap, String key) {
        if (ibaMap == null || !ibaMap.containsKey(key)) {
            return "";
        }
        List<String> values = ibaMap.get(key);
        return Objects.nonNull(values) ? values.get(0) : "";
    }

    private void outputExcel(HttpServletRequest request, HttpServletResponse response, byte[] bytes, String fileName)
            throws IOException {
        OutputStream outputStream = null;

        try {
            outputStream = ExcelUtils.getExportOutStream(request, response, fileName);
            outputStream.write(bytes);
            outputStream.flush();
            outputStream.close();
        } catch (Exception var10) {
            throw SystemErrorCodeEnum.INTERNAL_SERVER_ERROR.message();
        } finally {
            if (Objects.nonNull(outputStream)) {
                outputStream.flush();
                outputStream.close();
            }

        }
    }
}
